/* Copyright 2011 Gilberto Pacheco Gallegos.
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use
 * this file except in compliance with the License. You may obtain a copy of the
 * License at
 *     http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software distributed
 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
 * CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
 * language governing permissions and limitations under the License.  */
package net.ramptors.dominiobd.client.usuarios;

import com.google.gwt.cell.client.CheckboxCell;
import com.google.gwt.cell.client.FieldUpdater;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.editor.client.Editor;
import com.google.gwt.editor.client.SimpleBeanEditorDriver;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.user.cellview.client.Column;
import com.google.gwt.user.cellview.client.DataGrid;
import com.google.gwt.user.cellview.client.TextColumn;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.InlineHTML;
import com.google.gwt.user.client.ui.InlineLabel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ResizeComposite;
import com.google.gwt.user.client.ui.TabLayoutPanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.view.client.MultiSelectionModel;
import com.google.gwt.view.client.SelectionChangeEvent;
import com.google.gwt.view.client.SingleSelectionModel;
import com.google.web.bindery.event.shared.UmbrellaException;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import net.ramptors.dominiobd.client.ExcepcionDeAplicacion;
import net.ramptors.dominiobd.client.ServicioGrupos;
import net.ramptors.dominiobd.client.ServicioGruposAsync;
import net.ramptors.dominiobd.client.ServicioUsuarios;
import net.ramptors.dominiobd.client.ServicioUsuariosAsync;
import net.ramptors.dominiobd.client.entity.Grupo;
import net.ramptors.dominiobd.client.entity.Usuario;
import net.ramptors.dominiobd.client.entity.ValidadorDeUsuario;
import net.ramptors.dominiobd.client.entity.ValidadorDeUsuarioNuevo;

/**
 * @author Gilberto Pacheco Gallegos
 */
public class AbccUsuarios extends ResizeComposite implements Editor<Usuario> {
  private static AbccUsuariosUiBinder uiBinder =
      GWT.create(AbccUsuariosUiBinder.class);
  interface AbccUsuariosUiBinder extends UiBinder<Widget, AbccUsuarios> {
  }
  interface Driver extends SimpleBeanEditorDriver<Usuario, AbccUsuarios> {
  }
  private static final ValidadorDeUsuario validador = new ValidadorDeUsuario();
  private static final ValidadorDeUsuarioNuevo validadorDeNuevo =
      new ValidadorDeUsuarioNuevo();
  private final HashMap<String, Grupo> mapGrupos = new HashMap<String, Grupo>();
  private final Driver driver = GWT.create(Driver.class);
  protected Usuario modelo;
  private final ServicioUsuariosAsync proxyServicio =
      GWT.create(ServicioUsuarios.class);
  private final ServicioGruposAsync proxyServicioGruposAsync =
      GWT.create(ServicioGrupos.class);
  private final SingleSelectionModel<Usuario> modeloDeSeleccion =
      new SingleSelectionModel<Usuario>();
  {
    modeloDeSeleccion.addSelectionChangeHandler(new SelectionChangeEvent.Handler() {
      @Override
      public void onSelectionChange(final SelectionChangeEvent evento) {
        cambioDeSeleccion();
      }
    });
  }
  private final MultiSelectionModel<Grupo> seleccionDeGrupos =
      new MultiSelectionModel<Grupo>();
  @UiField(provided = true)
  DataGrid<Usuario> listado = new ListadoDeUsuarios(modeloDeSeleccion);
  @UiField @Ignore
  InlineLabel etiquetaTitulo;
  @UiField @Ignore
  InlineHTML etiquetaContrasenaObligatoria;
  @UiField @Ignore
  InlineHTML etiquetaRepetirContrasenaObligatoria;
  @UiField
  TextBox idEditor;
  @UiField
  TextBox contrasenaEditor;
  @UiField @Ignore
  TextBox textoContrasena2;
  @UiField
  TextBox nombresEditor;
  @UiField
  TextBox apellidosEditor;
  @UiField
  TextBox emailEditor;
  @UiField
  TabLayoutPanel tab;
  @UiField(provided = true)
  final DataGrid<Grupo> tablaGrupos = new DataGrid<Grupo>();
  {
    tablaGrupos.setEmptyTableWidget(new Label("No hay grupos registrados."));
    tablaGrupos.setSelectionModel(seleccionDeGrupos);
    tablaGrupos.addColumn(new Column<Grupo, Boolean>(new CheckboxCell(true, true)) {
      {
        setFieldUpdater(new FieldUpdater<Grupo, Boolean>() {
          @Override
          public void update(final int index, final Grupo objeto,
              final Boolean value) {
            seleccionDeGrupos.setSelected(objeto, value);
          }
        });
      }
      @Override
      public Boolean getValue(final Grupo grupo) {
        return seleccionDeGrupos.isSelected(grupo);
      }
    });
    tablaGrupos.addColumn(new TextColumn<Grupo>() {
      @Override
      public String getValue(final Grupo grupo) {
        return grupo.getId();
      }
    }, "Identificador");
    tablaGrupos.addColumn(new TextColumn<Grupo>() {
      @Override
      public String getValue(final Grupo grupo) {
        return grupo.getDescripcion();
      }
    }, "Descripción");
  }
  public AbccUsuarios() {
    initWidget(uiBinder.createAndBindUi(this));
    driver.initialize(this);
  }
  @Override
  protected void onAttach() {
    super.onAttach();
    // Para que IE8 e IE9 muestren bien el primer tab.
    tab.forceLayout();
  }
  public void actualiza() {
    buscaTodosLosGruposDeFormaAsincrona();
    // Para que IE7 muestre bien el primer tab.
    Scheduler.get().scheduleDeferred(new Scheduler.ScheduledCommand() {
      @Override
      public void execute() {
        tab.forceLayout();
      }
    });
  }
  @UiHandler("botonGuardar")
  void clicEnBotonGuardar(final ClickEvent evento) {
    guarda();
  }
  @UiHandler("botonEliminar")
  void clicEnBotonEliminar(final ClickEvent evento) {
    elimina();
  }
  @UiHandler({"botonNuevo", "botonCancelar"})
  void clicEnBotonNuevoOCancelar(final ClickEvent evento) {
    nuevo();
  }
  private void limpiaSeleccion() {
    final Usuario seleccion = modeloDeSeleccion.getSelectedObject();
    if (seleccion != null) {
      modeloDeSeleccion.setSelected(seleccion, false);
    }
  }
  private void cambioDeSeleccion() {
    final Usuario seleccion = modeloDeSeleccion.getSelectedObject();
    if (seleccion != null) {
      edita(seleccion);
    }
  }
  private void nuevo() {
    etiquetaTitulo.setText("Nuevo Usuario");
    idEditor.setReadOnly(false);
    etiquetaContrasenaObligatoria.setVisible(true);
    etiquetaRepetirContrasenaObligatoria.setVisible(true);
    limpiaSeleccion();
    muestra(new Usuario());
  }
  private void edita(final Usuario objeto) {
    final Usuario copia = new Usuario(objeto.getId(), objeto.getVersion(),
        objeto.getContrasena(), objeto.getNombres(), objeto.getApellidos(),
        objeto.getEmail(), objeto.getGrupos());
    etiquetaTitulo.setText(objeto.getId() + " - Editar Grupo");
    idEditor.setReadOnly(true);
    etiquetaContrasenaObligatoria.setVisible(false);
    etiquetaRepetirContrasenaObligatoria.setVisible(false);
    muestra(copia);
  }
  private void guarda() {
    try {
      leeModelo();
      if (modeloDeSeleccion.getSelectedObject() == null) {
        agregaDeFormaAsincrona();
      } else {
        modificaDeFormaAsincrona();
      }
    } catch (final ExcepcionDeAplicacion ex) {
      procesa(ex);
    }
  }
  private void elimina() {
    final Usuario seleccion = modeloDeSeleccion.getSelectedObject();
    if (seleccion != null && confirmaEliminarObjeto(seleccion)) {
      eliminaDeFormaAsincrona();
    }
  }
  private void muestra(final Usuario objeto) {
    this.modelo = objeto;
    driver.edit(objeto);
    textoContrasena2.setValue(null);
    asignaSeleccion(objeto);
  }
  private void asignaSeleccion(final Usuario objeto) {
    seleccionDeGrupos.clear();
    for (final String id : objeto.getGrupos()) {
      final Grupo grupo = mapGrupos.get(id);
      if (grupo != null) {
        seleccionDeGrupos.setSelected(grupo, true);
      }
    }
  }
  private void leeModelo() throws ExcepcionDeAplicacion {
    driver.flush();
    final HashSet<String> set = new HashSet<String>();
    for (Grupo grupo : seleccionDeGrupos.getSelectedSet()) {
      set.add(grupo.getId());
    }
    modelo.setGrupos(set);
    final String contrasena1 =
        modelo.getContrasena() == null ? "" : modelo.getContrasena();
    final String contrasena2 =
        textoContrasena2.getValue() == null ? "" : textoContrasena2.getValue();
    if (!contrasena1.equals(contrasena2)) {
      throw new ExcepcionDeAplicacion("Las contraeñas no coinciden.");
    }
  }
  private void llenaMap(final List<Grupo> grupos) {
    mapGrupos.clear();
    for (final Grupo grupo : grupos) {
      mapGrupos.put(grupo.getId(), grupo);
    }
  }
  private void buscaTodosLosGruposDeFormaAsincrona() {
    proxyServicioGruposAsync.buscaTodo(new AsyncCallback<List<Grupo>>() {
      @Override
      public void onFailure(final Throwable caught) {
        procesa(caught);
      }
      @Override
      public void onSuccess(final List<Grupo> lista) {
        tablaGrupos.setRowData(lista);
        llenaMap(lista);
        nuevo();
        buscaTodoDeFormaAsincrona();
      }
    });
  }
  private void buscaTodoDeFormaAsincrona() {
    proxyServicio.buscaTodo(new AsyncCallback<List<Usuario>>() {
      @Override
      public void onFailure(final Throwable caught) {
        procesa(caught);
      }
      @Override
      public void onSuccess(final List<Usuario> lista) {
        listado.setRowData(lista);
      }
    });
  }
  private void agregaDeFormaAsincrona() throws ExcepcionDeAplicacion {
    validadorDeNuevo.valida(modelo);
    proxyServicio.agrega(modelo, new AsyncCallback<Usuario>() {
      @Override
      public void onFailure(final Throwable caught) {
        procesa(caught);
      }
      @Override
      public void onSuccess(final Usuario resultado) {
        actualiza();
      }
    });
  }
  private void modificaDeFormaAsincrona() throws ExcepcionDeAplicacion {
    validador.valida(modelo);
    proxyServicio.modifica(modelo, new AsyncCallback<Usuario>() {
      @Override
      public void onFailure(final Throwable caught) {
        procesa(caught);
      }
      @Override
      public void onSuccess(final Usuario resultado) {
        actualiza();
      }
    });
  }
  private void eliminaDeFormaAsincrona() {
    proxyServicio.elimina(modelo.getId(), new AsyncCallback<Void>() {
      @Override
      public void onFailure(final Throwable caught) {
        procesa(caught);
      }
      @Override
      public void onSuccess(final Void resultado) {
        actualiza();
      }
    });
  }
  private boolean confirmaEliminarObjeto(final Usuario objeto) {
    final String mensaje = "¿Realmente desea eliminar este usuario?\n"
        + "\nId: " + objeto.getId()
        + "\nNombres: " + objeto.getNombres()
        + "\nApellidos: " + objeto.getApellidos()
        + "\nCorreo Electrónico: " + objeto.getEmail()
        + "\n\nNo podrá recuperarlo una vez eliminado."
        + "\n\nPresione Aceptar para eliminarlo o Cancelar para conservarlo.";
    return Window.confirm(mensaje);
  }
  private void procesa(final Throwable t) {
    throw new UmbrellaException(Collections.singleton(t));
  }
}